package zisu.netty.practice.NIO.GroupChat;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Scanner;

/**
 * @Desc: ""
 * @Author: caixiang
 * @DATE: 2020/7/2 15:47
 */
public class GroupChatServer {
    //定义属性
    private Selector selector;
    private ServerSocketChannel listenChannel;
    private static final int port = 6667;

    //构造器
    //初始化工作
    public GroupChatServer(){
        try {
            //得到选择器
            selector = Selector.open();
            //ServerSocketChannel
            listenChannel = ServerSocketChannel.open();
            //绑定端口 ( ServerSocketChannel去监听一端口 )
            listenChannel.socket().bind(new InetSocketAddress(port));
            //设置非阻塞模式
            listenChannel.configureBlocking(false);
            //将listenChannel注册到 selector里去,并选择  ServerSocketChannel 触发 selector来选择 的模式。。。只要listenChannel有事件触发 selector就会感知到
            listenChannel.register(selector, SelectionKey.OP_ACCEPT);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //监听
    public void listen(){
        try {
            //循环监听
            while (true){
                //循环2秒，，要是2s还没有 事件 过来就直接结束监听了。( 这里的count 就代表的是OP_ACCEPT事件 )
                //注意 selector.select() 会选择 这个selector下所有channel注册上来的  所发生的  事件
                int count = selector.select();
                //大于0 表示有事件要处理。
                if(count>0){
                    //遍历得到selectionKey 集合（这个集合 是所有发生的事件 的 channel 的集合）
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()){
                        //取出selectionKey
                        SelectionKey key = iterator.next();

                        //监听到了ACCEPT事件   （这个事件是上面设置的）
                        if(key.isAcceptable()){
                            //listenChannel 是一个serverChannel，当有连接事件上来的时候，listenChannel.accept()返回值 就是 当前连接上来客户端的socketChannel(是外部的)
                            //其实下面的sc 和 当前的key 是对应的。
                            SocketChannel sc = listenChannel.accept();
                            //设置非阻塞
                            sc.configureBlocking(false);
                            //将sc 注册到selector 上。，，注意这里是OP_READ 事件 并不是ServerSocketChannel的OP_ACCEPT事件
                            //每个channel 注册到 selector上 都可以选择事件类型（通告给selector的事件类型）
                            //把外部的socketChannel 注册到 Server端的 selector上。
                            sc.register(selector,SelectionKey.OP_READ);
                            //提示
                            System.out.println(sc.getRemoteAddress()+"上线");
                        }

                        //当有读模式 的channel 事件触发的时候 调用的是这个方法
                        // 当客户端 在socketChannel里面 写入数据的时候 就会触发 这里的isReadable
                        if(key.isReadable()){ //通道是可读的状态
                            //处理读
                            readData(key);
                        }

                        //当前key 删除，防止重复处理
                        //手动从集合中移动当前的selectionKey(这里是iterator)，防止重复操作，就是下次以后消息的时候 还会存在上次发消息的key
                        //注意每次有事件进来 并且已近处理完事件了，，那么一定要把 这个 selector.selectedKeys() 给删除。
                        //
                        iterator.remove();
                    }
                }else {
                    System.out.println("等待");
                }
            }
        }catch (Exception e){

        }finally {

        }
    }

    //读取客户端消息
    private void readData(SelectionKey selectionKey){
        //通过selectionKey 反向获取相应的channel，，定义一个 SocketChannel
        SocketChannel socketChannel = null;
        try {
            //取到 关联的channel
            socketChannel = (SocketChannel)selectionKey.channel();
            //创建buffer
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int count = socketChannel.read(buffer);
            if(count>0){
                //把缓冲区的数据转成字符串并输出。
                String msg = new String(buffer.array());
                //输出该消息
                System.out.println("from 客户端："+msg);

                //向其他的客户端转发消息(排除自己)
                sendInfoToOtherClient(msg,socketChannel);
            }
        }catch (Exception e){
            try {
                System.out.println(socketChannel.getRemoteAddress() +" 离线了.... ");
                //取消注册
                selectionKey.cancel();
                //关闭通道
                socketChannel.close();
            }catch (Exception e1){
                e1.printStackTrace();
            }
        }

    }

    //转发消息 给其他客户（给各个客户通道发）
    private void sendInfoToOtherClient(String msg,SocketChannel self) throws IOException {
        System.out.println("服务器转发消息。。。");
        //遍历所有注册到selector 上的SocketChannel 排除自己，并转发
        //selector.keys() 是代表所有注册到这个selector上的socketChannel、socketServerChannel
        for(SelectionKey key:selector.keys()){
            //通过key 取出对应的socket channel
            Channel targetChannel = key.channel();

            //排除自己
            if(targetChannel instanceof SocketChannel && targetChannel != self){

                SocketChannel socketChannel = (SocketChannel) targetChannel;
                //将msg 存储到 buffer
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                //将buffer 的数据 写入通道
                socketChannel.write(buffer);
            }

        }
    }

    public static void main(String[] args) {
        //创建服务器对象
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();
    }
}
